use std::collections::HashMap;
use std::sync::{Arc, Mutex};
use chrono::NaiveDate;
use tokio::sync::Mutex as AsyncMutex;
use crate::base::{exec_sqls, get_ch_pool, table_is_exist, DiviType, SqlList};

pub struct DiviJoin {
    divi_type: DiviType,
    locks: Arc<AsyncMutex<HashMap<String, Arc<Mutex<()>>>>>,
}

impl DiviJoin {
    pub fn new(divi_type: DiviType) -> Self {
        Self {
            divi_type,
            locks: Arc::new(AsyncMutex::new(HashMap::new())),
        }
    }

    async fn get_lock(&self, key: &str) -> Arc<Mutex<()>> {
        let mut locks = self.locks.lock().await;
        locks.entry(key.to_string()).or_insert_with(|| Arc::new(Mutex::new(()))).clone()
    }

    pub async fn get_table(&self, date: &NaiveDate, is_back: bool) -> anyhow::Result<String> {
        let date_str = date.format("%Y%m%d").to_string();
        let jname = format!("{}_{date_str}", self.divi_type.join_table_name());
        let mut client = get_ch_pool().get_handle().await?;
        let is_exist = table_is_exist(&jname).await?;
        if is_exist {
            return Ok(jname);
        }

        // 获取基于表名的锁
        let lock = self.get_lock(&jname).await;
        let _guard = lock.lock().unwrap();

        // 再次检查表是否存在
        let is_exist = table_is_exist(&jname).await?;
        if is_exist {
            return Ok(jname);
        }

        match &self.divi_type {
            DiviType::CompAcco | DiviType::IndiSave | DiviType::IndiLoan => {
                let str_sql = format!("CREATE TABLE IF NOT EXISTS {jname}(ID UInt64,DPIPS Array(UInt32),EMIDS Array(UInt32),DSVLS Array(Float64)) ENGINE = Join(ANY,LEFT,ID)");
                client.execute(&str_sql).await?;
                let str_sql=format!("truncate table {jname}");
                client.execute(str_sql).await?;
                let tname = match &self.divi_type {
                    DiviType::CompAcco => "T_COMP_ACCO_DIVI_ZIPPER",
                    DiviType::IndiSave => "T_INDI_ACCO_DIVI_ZIPPER",
                    DiviType::IndiLoan => "T_INDI_LOAN_DIVI_ZIPPER",
                    _ => unreachable!(),
                };
                let back_where = if is_back {
                    let btable = self.divi_type.back_table_name();
                    format!(
                        "AND ACID IN (SELECT ACID FROM {btable} WHERE DSDT<=toDate('{date_str}'))"
                    )
                } else {
                    "".to_string()
                };
                let str_sql=format!("WITH toDate('{date_str}') as DTID INSERT INTO {jname} SELECT ACID,DIVI.DPID,DIVI.EMID,DIVI.DSVL FROM {tname} WHERE DTID>=SDATE AND DTID<=EDATE {back_where}");
                client.execute(&str_sql).await?;
            }
            DiviType::IndiClie => {
                let sl = SqlList::from_file_name("indi_clie_ygfc");
                let btable = self.divi_type.back_table_name();
                let back_where = if is_back {
                    format!("AND CUID IN (SELECT CUID FROM {btable} WHERE DSDT<=toDate('{date_str}'))")
                } else {
                    "".to_string()
                };
                exec_sqls(&sl, &["{date}", "{back_where}"], &[&date_str, &back_where]).await?;
            }
            DiviType::CompBizFee | DiviType::IndiBizFee => {}
        }
        Ok(jname)
    }
    // pub async fn drop(&mut self,date: &NaiveDate) -> anyhow::Result<()> {
    //     let _lock = self.lock_me.lock().unwrap();
    //     let date_str = date.format("%Y%m%d").to_string();
    //     let jname = format!("{}_{date_str}", self.divi_type.join_table_name());
    //     let str_sql = format!("DROP TABLE IF EXISTS {jname}");
    //     let mut client = get_ch_pool().get_handle().await?;
    //     client.execute(&str_sql).await?;
    //     Ok(())
    // }
}


